#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <net/socket.h>
#include <logger/logger.h>

int sock_toaddr(const char * host, unsigned short port, struct sockaddr_in * addr)
{
	struct hostent * he;

	bzero(addr, sizeof(struct sockaddr_in));

	addr->sin_family = AF_INET;
	addr->sin_port = htons(port);

	if ((he = gethostbyname(host)) != NULL)
	{
		memcpy(&addr->sin_addr, he->h_addr, he->h_length);
	}
	else
	{
		if ((addr->sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
		{
			log_write(glog, "[Socket] invalid address %s.", host);
			return -1;
		}
	}
	return 0;
}

char * sock_addr(struct sockaddr_in * addr)
{
	static char tmp[MAXBUFFER];
	snprintf(tmp, MAXBUFFER, "%s:%d", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
	return tmp;
}

char * sock_ip(struct sockaddr_in * addr)
{
	static char tmp[MAXBUFFER];
	snprintf(tmp, MAXBUFFER, "%s", inet_ntoa(addr->sin_addr));
	return tmp;
}

char * sock_iip(unsigned int ip)
{
	struct sockaddr_in addr;
	addr.sin_addr.s_addr = ip;
	return sock_ip(&addr);
}

unsigned short sock_port(struct sockaddr_in * addr)
{
	return ntohs(addr->sin_port);
}

int sock_rbuf(t_socket so, int sz)
{
	if (setsockopt(so, SOL_SOCKET, SO_RCVBUF, (char*)&sz, sizeof(int)) == -1)
	{
		log_write(glog, "[Socket] setsockoption fd[%d] SO_RCVBUF %d failed, because %s.", so, sz, strerror(errno));
		return -1;
	}
	return 0;
}

int sock_wbuf(t_socket so, int sz)
{
	if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, (char*)&sz, sizeof(int)) == -1)
	{
		log_write(glog, "[Socket] setsockoption fd[%d] SO_SNDBUF %d failed, because %s.", so, sz, strerror(errno));
		return -1;
	}
	return 0;
}

int sock_reuse(t_socket so, int opt)
{
	if (setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(int)) < 0)
	{
		log_write(glog, "[Socket] set reuse fail. %s", strerror(errno));
		return -1;
	}
	return 0;
}

int sock_error(t_socket so)
{
	int error;
	socklen_t solen;

	solen = sizeof(int);

	if (getsockopt(so, SOL_SOCKET, SO_ERROR, &error, &solen) == -1)
	{
		log_write(glog, "[Socket] getsockopt fd=%d failed, because %s.", so, strerror(errno));
		return -1;
	}
	return error;
}

int sock_block(t_socket so, t_bool block)
{
	int ret = 0;

	if (block == FALSE)
	{
		ret = fcntl(so, F_SETFL, O_NONBLOCK);
	}
	else
	{
		ret = fcntl(so, F_SETFL, O_SYNC);
	}

	if (ret == -1)
	{
		log_write(glog, "[Socket] fcntl fd[%d] failed, because %s.", so, strerror(errno));
		return -1;
	}

	return 0;
}
